﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Linq;

using EmbeddedSensorCloud.Core.Server;
using EmbeddedSensorCloud.Core.Plugin;
using EmbeddedSensorCloud.Log;
using EmbeddedSensorCloud.Plugin;
using System.Reflection;
using System.Diagnostics;
using EmbeddedSensorCloud.Console.Commands;
using System.Net.Sockets;
using EmbeddedSensorCloud.HTTP;
using EmbeddedSensorCloud.Command;
using System.Security;
using System.Security.Permissions;

namespace EmbeddedSensorCloud.Console
{
    class Program
    {
        private static List<LogBase> loggers;
        private static PluginCore pluginCore;
        private static ServerCore serverCore;

        static void Main(string[] args)
        {
            #region Loggers
            loggers = new List<LogBase>();
            loggers.Add(new ConsoleLog());
            #endregion

            pluginCore = new PluginCore();
            serverCore = new ServerCore(clientListener_NewClient);
            serverCore.ServerStatusChanged += ChangeLogState;

            #region Commands
            List<CommandBase> cmdList = new List<CommandBase>();
            CommandBase.Logged += (e) => WriteToLoggers(e);
            cmdList.Add(new LogFileCommand(loggers));
            cmdList.Add(new ServerCommand(serverCore));
            cmdList.Add(new PluginCommand(pluginCore));
            cmdList.Add(new ExitCommand(serverCore));
            #endregion

            System.Console.WriteLine("EmbeddedSensorCloud Server [Version 1.0.1234]");
            System.Console.WriteLine("(c) 2013 Sebastian Szukiewicz & Viktor Hofer. All rights reserved.");
            System.Console.WriteLine();
            System.Console.Write("Command: ");

            #region Input file handling
            if (args.Length > 0 && File.Exists(args[0]))
            {
                string[] lines = File.ReadAllLines(args[0]);
                foreach (var line in lines)
                {
                    ReadCommand(cmdList, line);
                }
            }
            #endregion

            while (true)
            {
                string command = System.Console.ReadLine();
                ReadCommand(cmdList, command);
            }
        }
        
        private static void ReadCommand(IEnumerable<CommandBase> cmdList, string command)
        {
            var cmd = cmdList.FirstOrDefault(C => C.CanExecute(command));
            if (cmd != null)
            {
                cmd.Execute(command);
            }
            else
            {
                string helpText = cmdList.Aggregate("Unknown command. Available commands:", (current, next) => current + Environment.NewLine + next);
                System.Console.WriteLine(helpText);
            }
            
            System.Console.SetCursorPosition(0, System.Console.CursorTop);
            System.Console.Write("Command: ");
        }

        private static void ChangeLogState(dynamic e)
        {
            string message = string.Empty;

            if (e is ClientOpenedEventArgs)
            {
                message = "Client connected on Endpoint " + e.IP + ":" + e.Port;
            }
            else if (e is ClientClosedEventArgs)
            {
                message = "Client closed on Endpoint " + e.IP + ":" + e.Port;
            }
            else if (e is RespondedEventArgs)
            {
                message = "Response sent";
            }
            else if (e is RequestParsedEventArgs)
            {
                message = "Request " + e.Url + " parsed.";
            }
            else if (e is RequestParsingFailedEventArgs)
            {
                message = "Request parsing failed.";
            }

            WriteToLoggers(message);
        }

        private static void WriteToLoggers(string message)
        {
            message = string.Format("{0} - {1}", DateTime.Now.ToString("HH:mm:ss"), message);

            foreach (var logger in loggers)
            {
                if (logger is ConsoleLog)
                {
                    System.Console.SetCursorPosition(0, System.Console.CursorTop);
                }

                logger.WriteLine(message);

                if (logger is ConsoleLog)
                {
                    System.Console.Write("Command: ");
                }
            }
        }

        private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
        {
            serverCore.Stop();
            Environment.Exit(0);
        }

        private static void clientListener_NewClient(NetworkStream stream, Request request)
        {
            var suitablePlugins = new List<AvailablePlugin>();

            foreach (var plugin in pluginCore.Plugins)
            {
                try
                {
                    if (plugin.Instance.IsResponseable(request))
                    {
                        suitablePlugins.Add(plugin);
                    }
                }
                catch { continue; }
            }

            int count = suitablePlugins.Count();

            Response response = null;

            if (count > 1)
            {
                // Internal Error Code 500
                WriteToLoggers(string.Format("Too many plugins suitable for handling the request '{0}'.", request.Url));
                response = new Response(HttpStatusCode.InternalServerError);
            }
            else if (count == 1)
            {
                var suitablePlugin = suitablePlugins.First();

                try
                {
                    response = suitablePlugin.Instance.Respond(request);
                    if (response == null)
                    {
                        throw new ArgumentNullException();
                    }
                    WriteToLoggers(string.Format("Plugin '{0}' v{1} executed.", suitablePlugin.Name, suitablePlugin.Version));
                }
                catch (Exception ex)
                {
                    WriteToLoggers(string.Format("Plugin '{0}' v{1} execution failed:{2}   * {3} *", 
                        suitablePlugin.Name, 
                        suitablePlugin.Version, 
                        Environment.NewLine, 
                        ex.Message));

                    response = new Response(HttpStatusCode.InternalServerError);
                }
            }
            else
            {
                // File Not Found 404
                WriteToLoggers(string.Format("No suitable plugin found for handling the request '{0}'.", request.Url));
                response = new Response(HttpStatusCode.NotFound);
            }

            response.Send(stream);
            ChangeLogState(new RespondedEventArgs());
        }
    }
}
